<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title> 深入解析变量声明提升和函数声明提升 </title>
</head>
<body>
    
</body>
</html>
<script>
//js运行阶段 
  /*
    js的运行阶段分为
        预编译阶段
        执行阶段 
    变量提升和函数声明提升 就是在这个阶段提升      
    在预编译阶段 js引擎会做一些事情 即读取变量的定义 并确定其作用域的生效范围   
  */
  /*
    变量定义 
    使用var关键字定义变量 并未赋值的情况下 该变量的值未undefined
    变量作用域
    全局变量的作用域遍布全局  
    局部变量的作用域仅在于函数内部 
    函数内部的同名变量 或 参数其优先级高于全局同名变量 
  */


	// 作用域:变量起作用的区域
	// 在JS中 var和function定义的变量是根据 函数划分作用域
	// 定义在局部作用域中的变量就是局部变量。局部变量仅限于函数内部使用 . 出了函数 就被销毁  
    // 定义在函数外部的变量就是全局变量  全局变量可以在任何地方使用    
    // 当全局变量和局部变量同名时，全局变量不会作用于局部变量的变量
    // 在局部环境中优先使用局部变量
	// 定义变量时 省略了var关键字  则变量会成为全局变量
    
    //  var和let-const区别?     
	//  var定义的变量可以变量提升     (也叫域解析 域加载 ...)  //在声明变量之前可调用 为undefined 
	//  var定义的变量会成为window属性       
	//  只被函数限制作用域




   //变量声明提升
   //来看一个demo代码如下 
    var name = 'ryan';
    function say(){
    console.log(name); //输出：undefined
    var name = 'zoe';
    console.log(name); //输出：'zoe'
    }
    say(); 
    /*解析：上述代码从结果看，say函数执行第一次打印name时，
    并未打印全局的name('ryan')，而是打印局部的name（undefined），
    这是因为在预编译阶段，say函数内部进行了变量声明提升，
    提升后的执行效果如下：    */               
    var name = 'ryan';
    function say(){
    var name;              //变量name声明提升至作用域顶部，但未赋值，故为undefined
    console.log(name);     //存在局部name，则无视全局name
    name = 'zoe';          //变量赋值保持原位
    console.log(name);     //输出：'zoe'
    }
    say(); 


//函数声明提升 
    //函数声明   //函数表达式 
     
//函数声明 (声明式函数)
    say(); //输出 "saying"
    function say(){
        console.log("saying")
    }
//函数表达式 (赋值式函数 赋值给一个变量)
    say(); //报错 say is not function
    var say = function(){
        console.log("saying")
    }
//原因在于，通过函数声明的方式，该函数声明（包括定义）会被提升至作用域的顶部，
//表达式的创建方式则只提升了变量say至作用域的顶部，此时的say其值为undefined，调用say()自然报错“say不是一个方法”。



var say = function(){
  console.log('1');
};
function say(){
  console.log('2');
};
say();               //输出：'1'  //预编译阶段进行变量声明提升和函数声明提升后，上述代码执行效果等同于

var say;             //变量声明提升
function say(){      //函数声明提升
  console.log('2');
}

say = function(){    //变量赋值保持原位执行，say函数被覆盖
  console.log('1');
};
say();               //输出'1'


// 函数声明提升，会将函数的声明和定义全都提升至作用域顶部。
// 变量声明提升，只提升声明部分（未赋值状态），赋值部分保持原位置不动。


// 函数声明提升的优先级要高于变量声明提升。
// 先看一个简单示例：
console.log(say); //输出：[Function: say]
function say(){
  console.log('1');
};
var say = '2';
console.log(say); //输出'2"
/*本例中声明的函数和变量同名都是say，且函数声明在先，变量声明在后，
  按理说第一次打印say值预期会是undefined 然而结果是[Function: say]。*/


//预编译阶段进行变量声明提升和函数声明提升后，上述代码执行效果等同于：
var say = function (){   //函数声明（包括定义）提升
  console.log('1');
};
var say;                 //只是声明，并不会覆盖say的值
console.log(say);        //故输出：[Function: say]
say = '2';               //此时say会被覆盖
console.log(say);        //输出'2'

//同名情况下，函数声明提升优先级要高于变量声明提升，且提升后该函数声明定义不会被提升后的同名变量声明所覆盖，
//但是会被后续顺序执行的同名变量赋值所覆盖。



























































     
</script>